package com.tools.web;

import com.tools.common.container.r.OnlyReadArrayList;
import com.tools.common.exception.IORuntimeException;
import com.tools.common.exception.ParseRuntimeException;
import com.tools.common.io.CharsetEnum;
import com.tools.common.io.IOKit;
import com.tools.common.object.Note;
import com.tools.common.object.string.StrKit;
import com.tools.common.security.SysKit;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;

/**
 * Servlet 一些请求、响应对象
 * 的常用操作的工具类
 * */
@Note("Servlet 一些请求、响应对象的常用操作的工具类")
class ServletKit {

    ServletKit() {}


    @Note("从 MultipartFile 中获取 InputStream 字节输入流")
    public static InputStream multipartInputStream(MultipartFile file) {
        try {
            return file.getInputStream();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("获取请求体的 InputStream 字节输入流")
    public static ServletInputStream requestInputStream(ServletRequest request) {
        try {
            return request.getInputStream();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("获取请求体的 BufferedReader 字符缓冲输入流")
    public static BufferedReader requestBufferedReader(ServletRequest request) {
        try {
            return request.getReader();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("获取写入响应体的 OutputStream 字节输出流")
    public static ServletOutputStream responseOutputStream(ServletResponse response) {
        try {
            return response.getOutputStream();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("获取写入响应体的 PrintWriter 字符输出流")
    public static PrintWriter responsePrintWriter(ServletResponse response) {
        try {
            return response.getWriter();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("响应二进制流数据时 HttpServletResponse 需要设置一些东西")
    public static void responseWriteStreamSetting(String contentType, HttpServletResponse response) {
        response.setContentType(contentType);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "No-cache");//防止缓存
        response.setDateHeader("Expires", 0);   //设置缓存过期时间 0 秒，即不缓存
    }



    @Note("把图片对象转为字节流写入到 HTTP 的响应输出流中，返回给 Web 客户端（imgSuffix 是图片的后缀，不要加【.】）")
    public static void responseWriteImage(BufferedImage image, String imgSuffix, HttpServletResponse response) {
        responseWriteStreamSetting("image/" + imgSuffix, response);
        ServletOutputStream servletOutput = responseOutputStream(response);
        try {
            ImageIO.write(image, imgSuffix, servletOutput);
        } catch (Exception e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("Web 服务器响应下载文件给客户端时所必须做的一部分设置操作（设置文件类型、文件名称、编码等）")
    public static void responseDownload(String filename, String mimeType, CharsetEnum charsetEnum, HttpServletResponse response){
        if(filename == null || filename.isEmpty()) throw new IllegalArgumentException("下载的文件名称不能为空");
        try {
            String charset = (charsetEnum == null) ? CharsetEnum.UTF_8.getValue() : charsetEnum.getValue();
            response.setCharacterEncoding(charset);
            response.setHeader("content-Type", mimeType);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, charset));
            openHeader(response, "Content-Disposition");
        } catch (UnsupportedEncodingException e) {
            throw new ParseRuntimeException(e);
        }
    }

    @Note("使用 HttpServletResponse 开放自定义响应头给客户端阅读和获取")
    public static void openHeader(HttpServletResponse response, String header) {
        if(response == null || StrKit.isBlank(header)) return;
        String openHeaders = private_getOpenHeaders(response);
        response.setHeader("Access-Control-Expose-Headers", openHeaders + header);
    }


    @Note("使用 HttpServletResponse 开放响应头给客户端阅读和获取，多个响应头之间用,分隔")
    public static void openHeaders(HttpServletResponse response, String... headers) {
        if(response == null || headers == null || headers.length == 0){
            return;
        }
        String openHeaders = private_getOpenHeaders(response);
        response.setHeader("Access-Control-Expose-Headers", openHeaders + StrKit.toString(",", headers));
    }


    @Note("使用 HttpServletResponse 开放响应头给客户端阅读和获取，多个响应头之间用,分隔")
    public static void openHeaders(HttpServletResponse response, Iterable<String> headers) {
        if(response == null || headers == null){
            return;
        }
        String openHeaders = private_getOpenHeaders(response);
        response.setHeader("Access-Control-Expose-Headers", openHeaders + StrKit.toString(",", headers));
    }


    @Note("把 MultipartFile 对象转化为 File 对象")
    public static File multipartFileToFile(MultipartFile multiFile) {
        if(multiFile == null){
            throw new IORuntimeException("参数 MultipartFile 对象不能为 null ");
        }
        String filename = multiFile.getOriginalFilename();
        if(filename == null) {
            filename = "";
        }
        String suffix = private_getFileSuffix(filename);
        File newFile;
        try {
            newFile = File.createTempFile(filename, suffix);
            multiFile.transferTo(newFile);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
        return newFile;
    }


    @Note("把 File 转为 MultipartFile，若 fileKey 为空则取文件名称作为 fileKey")
    public static MultipartFile fileToMultipartFile(String fileKey, File file, String contentType) {
        if(file == null){
            throw new IORuntimeException("源文件 File 不能为 null ");
        }
        String filename = file.getName();
        if(fileKey == null || fileKey.isEmpty()) {
            fileKey = filename;
        }
        try {
            return new MockMultipartFile(fileKey, filename, contentType, IOKit.fileInputStream(file));
        } catch (Exception e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("@return 从 HTTP 的请求头中获取客户端的 IP 信息")
    public static String clientIP(HttpServletRequest request) {
        String ipAddress = getProxyHeader(request);
        return buildClientIPByHeader(ipAddress, request);
    }


    @Note("根据已经过校验的代理 header 构建 IP 信息，如果为空则获取直连主机地址")
    public static String buildClientIPByHeader(String addressHeader, HttpServletRequest request) {
        if(addressHeader == null) {
            addressHeader = request.getRemoteAddr();
            if(addressHeader.equals("127.0.0.1") || addressHeader.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                addressHeader = SysKit.LOCAL_FIRST_IPV4;
            }
        }
        //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        //IPv4 【xxx.xxx.xxx.xxx】 的长度为 15
        if(addressHeader != null && addressHeader.length() > 15) {
            if(addressHeader.indexOf(",") > 0){
                addressHeader = addressHeader.substring(0, addressHeader.indexOf(","));
            }
        }
        return addressHeader;
    }


    @Note("获取此次请求的所有代理 IP")
    public static OnlyReadArrayList<String> allProxyIP(HttpServletRequest request) {
        String ips = getProxyHeader(request);
        if(ips == null) {
            ips = request.getRemoteAddr();
            if(ips.equals("127.0.0.1") || ips.equals("0:0:0:0:0:0:0:1")){
                ips = SysKit.LOCAL_FIRST_IPV4;
            }
            return new OnlyReadArrayList<>(ips);
        }
        return new OnlyReadArrayList<>(ips.split(","));
    }


    @Note("返回携带多层代理 IP 及客户端 IP 的请求头数据")
    public static String getProxyHeader(HttpServletRequest request) {
        String header = request.getHeader("x-forwarded-for");
        if(header != null && !header.isEmpty() && !"unknown".equalsIgnoreCase(header)) {
            return header;
        }
        header = request.getHeader("Proxy-Client-IP");
        if(header != null && !header.isEmpty() && !"unknown".equalsIgnoreCase(header)) {
            return header;
        }
        header = request.getHeader("WL-Proxy-Client-IP");
        if(header != null && !header.isEmpty() && !"unknown".equalsIgnoreCase(header)) {
            return header;
        }
        return null;
    }


    @Note("获取该次请求源头的 URL 地址")
    public static String referer(HttpServletRequest request) {
        String referer = request.getHeader("referer");
        if(referer == null || referer.isEmpty()){
            return request.getScheme() + "://" +
                    WebKit.getClientIP() + ":" +
                    request.getRemotePort() + "/";
        }
        return referer;
    }

    @Note("获取当前请求的设备信息请求头")
    public static String userAgent(HttpServletRequest request) {
        return request.getHeader("User-Agent");
    }

    /* ********************************************************************************************
     *
     *          私有逻辑
     *
     * *********************************************************************************************
     * */

    @Note("获取文件的 【.后缀名称】 ")
    private static String private_getFileSuffix(String filename) {
        int index = filename.lastIndexOf(".");
        return (index < 0) ? "" : filename.substring(index);
    }


    @Note("获取已开放的请求头")
    private static String private_getOpenHeaders(HttpServletResponse response) {
        String openHeaders = response.getHeader("Access-Control-Expose-Headers");
        return (openHeaders == null) ? "" : (openHeaders + ",");
    }

}
